package com.hopes.bizz.close.gis.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.service.IService;
import com.hopes.bizz.close.basic.entity.ClosedGeneralCarEntity;
import com.hopes.bizz.close.basic.entity.ClosedPersonEntity;
import com.hopes.bizz.close.basic.service.ClosedGeneralCarService;
import com.hopes.bizz.close.basic.service.ClosedPersonService;
import com.hopes.bizz.close.common.constant.BasicDicConstant;
import com.hopes.bizz.close.common.constant.GisRequestEnums;
import com.hopes.bizz.close.common.constant.ParkCloseProperty;
import com.hopes.bizz.close.common.constant.VisitorDicConstant;
import com.hopes.bizz.close.common.constant.enums.ParkCloseModDictEnum;
import com.hopes.bizz.close.gis.bo.ApplyAuditBO;
import com.hopes.bizz.close.gis.bo.HazardCarBO;
import com.hopes.bizz.close.gis.service.ClosedInOutService;
import com.hopes.bizz.close.gis.vo.*;
import com.hopes.bizz.close.hazard.entity.ClosedHazardCarEntity;
import com.hopes.bizz.close.hazard.service.ClosedHazardCarService;
import com.hopes.bizz.close.visitor.entity.ClosedGeneralCarRecordEntity;
import com.hopes.bizz.close.visitor.entity.ClosedHazardCarRecordEntity;
import com.hopes.bizz.close.visitor.entity.ClosedPersonRecordEntity;
import com.hopes.bizz.close.visitor.service.ClosedGeneralCarRecordService;
import com.hopes.bizz.close.visitor.service.ClosedHazardCarRecordService;
import com.hopes.bizz.close.visitor.service.ClosedPersonRecordService;
import com.hopes.bizz.common.core.constant.CommonEnum;
import com.hopes.bizz.common.core.util.DateUtils;
import com.hopes.boss.admin.api.entity.SysDictItem;
import com.hopes.boss.admin.api.resolver.DictResolver;
import com.hopes.boss.common.core.constant.CommonConstants;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author yumr
 * @ProjectName bizz
 * @Description 封闭化管理一张图
 * @time 2023/10/26 15:04
 */
@Service
@RequiredArgsConstructor
public class ClosedInOutServiceImpl implements ClosedInOutService {

    private final ParkCloseProperty parkCloseProperty;
    private final ClosedPersonService closedPersonService;
    private final ClosedHazardCarService closedHazardCarService;
    private final ClosedGeneralCarService closedGeneralCarService;

    private final ClosedPersonRecordService closedPersonRecordService;
    private final ClosedHazardCarRecordService closedHazardCarRecordService;
    private final ClosedGeneralCarRecordService closedGeneralCarRecordService;

    @Override
    public InOutParkVO queryHazardCar(GisRequestEnums.GisCalEnum gisCalEnum) {
        final InOutParkVO inOutParkVO = new InOutParkVO();
        if (ObjUtil.isNull(gisCalEnum)) {
            return inOutParkVO;
        }
        final Calendar instance = Calendar.getInstance();
        // 申请车次、审批车次、园内车次
        final LambdaQueryWrapper<ClosedHazardCarEntity> applyWrapper = buildQueryWrapper(ClosedHazardCarEntity::getApplyTime, instance, gisCalEnum);
        final LambdaQueryWrapper<ClosedHazardCarEntity> auditWrapper = buildQueryWrapper(ClosedHazardCarEntity::getAuditTime, instance, gisCalEnum);
        final LambdaQueryWrapper<ClosedHazardCarEntity> insideWrapper = Wrappers.<ClosedHazardCarEntity>lambdaQuery()
                .eq(ClosedHazardCarEntity::getUseFlag, CommonConstants.DEFAULT_TRUE_FLAG).eq(ClosedHazardCarEntity::getInParkFlag, CommonConstants.DEFAULT_TRUE_FLAG);
        final ApplyAuditBO applyAuditBO = queryApplyAuditVO(closedHazardCarService, applyWrapper, auditWrapper, Boolean.TRUE, insideWrapper);
        BeanUtils.copyProperties(applyAuditBO, inOutParkVO);
        // 危险品出入园车次
        final Map<Integer, Integer> enterOuterMap = queryEnterOuterVO(closedHazardCarRecordService, ClosedHazardCarRecordEntity::getInOutTime, ClosedHazardCarRecordEntity::getDirection, instance, gisCalEnum);
        inOutParkVO.setEnter(MapUtil.getInt(enterOuterMap, VisitorDicConstant.CLOSE_VISITOR_DIRECTION_IN, 0));
        inOutParkVO.setOuter(MapUtil.getInt(enterOuterMap, VisitorDicConstant.CLOSE_VISITOR_DIRECTION_OUT, 0));
        return inOutParkVO;
    }

    @Override
    public PersonGeneralCarVO queryGeneralCar(GisRequestEnums.GisCalEnum gisCalEnum) {
        final PersonGeneralCarVO resultVO = new PersonGeneralCarVO();
        if (ObjUtil.isNull(gisCalEnum)) {
            return resultVO;
        }
        final Calendar instance = Calendar.getInstance();
        // 普通车辆入园车次、出园车次
        final Map<Integer, Integer> enterOuterMap = queryEnterOuterVO(closedGeneralCarRecordService, ClosedGeneralCarRecordEntity::getInOutTime, ClosedGeneralCarRecordEntity::getDirection, instance, gisCalEnum);
        resultVO.setEnter(MapUtil.getInt(enterOuterMap, VisitorDicConstant.CLOSE_VISITOR_DIRECTION_IN, 0));
        resultVO.setOuter(MapUtil.getInt(enterOuterMap, VisitorDicConstant.CLOSE_VISITOR_DIRECTION_OUT, 0));

        // 长期申请、审批
        final LambdaQueryWrapper<ClosedGeneralCarEntity> longTimeApplyWrapper = buildQueryWrapper(ClosedGeneralCarEntity::getApplyTime, instance, gisCalEnum).eq(ClosedGeneralCarEntity::getCarType, BasicDicConstant.CLOSE_GENERAL_CAR_TYPE_COMPANY);
        final LambdaQueryWrapper<ClosedGeneralCarEntity> longTimeAuditWrapper = buildQueryWrapper(ClosedGeneralCarEntity::getAuditTime, instance, gisCalEnum).eq(ClosedGeneralCarEntity::getCarType, BasicDicConstant.CLOSE_GENERAL_CAR_TYPE_COMPANY);
        final ApplyAuditBO longTimeBO = queryApplyAuditVO(closedGeneralCarService, longTimeApplyWrapper, longTimeAuditWrapper, Boolean.FALSE, null);

        // 临时申请、审批
        final LambdaQueryWrapper<ClosedGeneralCarEntity> shortTimeApplyWrapper = buildQueryWrapper(ClosedGeneralCarEntity::getApplyTime, instance, gisCalEnum).eq(ClosedGeneralCarEntity::getCarType, BasicDicConstant.CLOSE_GENERAL_CAR_TYPE_VISITOR);
        final LambdaQueryWrapper<ClosedGeneralCarEntity> shortTimeAuditWrapper = buildQueryWrapper(ClosedGeneralCarEntity::getAuditTime, instance, gisCalEnum).eq(ClosedGeneralCarEntity::getCarType, BasicDicConstant.CLOSE_GENERAL_CAR_TYPE_VISITOR);
        final ApplyAuditBO shortTimeBO = queryApplyAuditVO(closedGeneralCarService, shortTimeApplyWrapper, shortTimeAuditWrapper, Boolean.FALSE, null);

        resultVO.setLongTimeApplyAudit(longTimeBO);
        resultVO.setShortTimeApplyAudit(shortTimeBO);
        return resultVO;
    }

    @Override
    public PersonGeneralCarVO queryPerson(GisRequestEnums.GisCalEnum gisCalEnum) {
        final PersonGeneralCarVO resultVO = new PersonGeneralCarVO();
        if (ObjUtil.isNull(gisCalEnum)) {
            return resultVO;
        }
        final Calendar instance = Calendar.getInstance();
        // 园区人员 入园人数、出园人数
        final Map<Integer, Integer> enterOuterMap = queryEnterOuterVO(closedPersonRecordService, ClosedPersonRecordEntity::getInOutTime, ClosedPersonRecordEntity::getDirection, instance, gisCalEnum);
        resultVO.setEnter(MapUtil.getInt(enterOuterMap, VisitorDicConstant.CLOSE_VISITOR_DIRECTION_IN, 0));
        resultVO.setOuter(MapUtil.getInt(enterOuterMap, VisitorDicConstant.CLOSE_VISITOR_DIRECTION_OUT, 0));

        // 长期申请、审批
        final LambdaQueryWrapper<ClosedPersonEntity> longTimeApplyWrapper = buildQueryWrapper(ClosedPersonEntity::getApplyTime, instance, gisCalEnum).eq(ClosedPersonEntity::getPersonType, BasicDicConstant.CLOSE_PERSON_TYPE_COMPANY);
        final LambdaQueryWrapper<ClosedPersonEntity> longTimeAuditWrapper = buildQueryWrapper(ClosedPersonEntity::getAuditTime, instance, gisCalEnum).eq(ClosedPersonEntity::getPersonType, BasicDicConstant.CLOSE_PERSON_TYPE_VISITOR);
        final ApplyAuditBO longTimeBO = queryApplyAuditVO(closedPersonService, longTimeApplyWrapper, longTimeAuditWrapper, Boolean.FALSE, null);

        // 短期申请、审批
        final LambdaQueryWrapper<ClosedPersonEntity> shortTimeApplyWrapper = buildQueryWrapper(ClosedPersonEntity::getApplyTime, instance, gisCalEnum).eq(ClosedPersonEntity::getPersonType, BasicDicConstant.CLOSE_PERSON_TYPE_VISITOR);
        final LambdaQueryWrapper<ClosedPersonEntity> shortTimeAuditWrapper = buildQueryWrapper(ClosedPersonEntity::getAuditTime, instance, gisCalEnum).eq(ClosedPersonEntity::getPersonType, BasicDicConstant.CLOSE_PERSON_TYPE_VISITOR);
        final ApplyAuditBO shortTimeBO = queryApplyAuditVO(closedPersonService, shortTimeApplyWrapper, shortTimeAuditWrapper, Boolean.FALSE, null);

        resultVO.setLongTimeApplyAudit(longTimeBO);
        resultVO.setShortTimeApplyAudit(shortTimeBO);
        return resultVO;
    }

    @Override
    public InOutParkChartVO queryPersonChart(String beginTimeStr, String endTimeStr) {
        final InOutParkChartVO inOutParkChartVO = new InOutParkChartVO();
        final List<SysDictItem> closeAccessList = DictResolver.getItemsByType(ParkCloseModDictEnum.closed_access_control);
        if (CollUtil.isEmpty(closeAccessList)) {
            return inOutParkChartVO;
        }
        final Date endTime = DateUtils.parseDate(CommonEnum.StartEndEnum.END, endTimeStr);
        final Date beginTime = DateUtils.parseDate(CommonEnum.StartEndEnum.START, beginTimeStr);
        inOutParkChartVO.getGateAccessVoList().addAll(queryAccessGateVoList(closedPersonRecordService, ClosedPersonRecordEntity::getInOutTime, ClosedPersonRecordEntity::getAccessId, ClosedPersonRecordEntity::getDirection, closeAccessList, beginTime, endTime));
        inOutParkChartVO.getPersonCarVoList().addAll(queryPersonCarVoList(closedPersonService, closedPersonRecordService, ClosedPersonEntity::getApplyTime, ClosedPersonEntity::getAuditTime, ClosedPersonRecordEntity::getInOutTime, ClosedPersonRecordEntity::getDirection, beginTime, endTime));
        return inOutParkChartVO;
    }

    @Override
    public List<GateCarVO> queryGateCar(GisRequestEnums.GisCalEnum gisCalEnum) {
        final List<GateCarVO> gateCarVOList = new ArrayList<>();
        final List<SysDictItem> closeGateList = DictResolver.getItemsByType(ParkCloseModDictEnum.closed_gate);
        if (ObjUtil.isNull(gisCalEnum) || CollUtil.isEmpty(closeGateList)) {
            return gateCarVOList;
        }
        // 1、危险品车次
        final LambdaQueryWrapper<ClosedHazardCarRecordEntity> hazardCarWrapper = Wrappers.<ClosedHazardCarRecordEntity>lambdaQuery().isNotNull(ClosedHazardCarRecordEntity::getGateId);
        final Map<Long, Long> hazardLongMap = groupCarInOutParkRecordByGateId(closedHazardCarRecordService, hazardCarWrapper, ClosedHazardCarRecordEntity::getGateId);
        // 2、企业车次
        final LambdaQueryWrapper<ClosedGeneralCarRecordEntity> companyCarWrapper = Wrappers.<ClosedGeneralCarRecordEntity>lambdaQuery().isNotNull(ClosedGeneralCarRecordEntity::getGateId).eq(ClosedGeneralCarRecordEntity::getCarType, BasicDicConstant.CLOSE_GENERAL_CAR_TYPE_COMPANY);
        final Map<Long, Long> companyLongMap = groupCarInOutParkRecordByGateId(closedGeneralCarRecordService, companyCarWrapper, ClosedGeneralCarRecordEntity::getGateId);
        // 3、访客车次
        final LambdaQueryWrapper<ClosedGeneralCarRecordEntity> visitorCarWrapper = Wrappers.<ClosedGeneralCarRecordEntity>lambdaQuery().isNotNull(ClosedGeneralCarRecordEntity::getGateId).eq(ClosedGeneralCarRecordEntity::getCarType, BasicDicConstant.CLOSE_GENERAL_CAR_TYPE_VISITOR);
        final Map<Long, Long> visitorLongMap = groupCarInOutParkRecordByGateId(closedGeneralCarRecordService, visitorCarWrapper, ClosedGeneralCarRecordEntity::getGateId);
        for (SysDictItem sysDictItem : closeGateList) {
            final GateCarVO gateCarVO = new GateCarVO();
            gateCarVO.setIndex(sysDictItem.getName());
            gateCarVO.setHazardNum(MapUtil.getInt(hazardLongMap, sysDictItem.getId(), 0));
            gateCarVO.setCompanyNum(MapUtil.getInt(companyLongMap, sysDictItem.getId(), 0));
            gateCarVO.setVisitorNum(MapUtil.getInt(visitorLongMap, sysDictItem.getId(), 0));
            gateCarVOList.add(gateCarVO);
        }
        return gateCarVOList;
    }

    /**
     * 根据卡口分类危险品车辆记录
     * @return  分类结果
     */
    private <T, R> Map<R, Long> groupCarInOutParkRecordByGateId(IService<T> service, Wrapper<T> wrapper, SFunction<T, R> groupMapper) {
        final List<T> queryList = service.list(Wrappers.<T>lambdaQuery().isNotNull(groupMapper));
        if (CollUtil.isEmpty(queryList)) {
            return null;
        }
        return queryList.stream().filter(e -> ObjUtil.isNotNull(groupMapper.apply(e))).collect(Collectors.groupingBy(groupMapper, Collectors.counting()));
    }



    @Override
    public InOutParkChartVO queryGeneralHazardCarChart(GisRequestEnums.GenHazardEnum genHazardEnum, String beginTimeStr, String endTimeStr) {
        final InOutParkChartVO inOutParkChartVO = new InOutParkChartVO();
        final List<SysDictItem> closeGateList = DictResolver.getItemsByType(ParkCloseModDictEnum.closed_gate);
        if (CollUtil.isEmpty(closeGateList)) {
            return inOutParkChartVO;
        }
        final Date endTime = DateUtils.parseDate(CommonEnum.StartEndEnum.END, endTimeStr);
        final Date beginTime = DateUtils.parseDate(CommonEnum.StartEndEnum.START, beginTimeStr);

        if (genHazardEnum.equals(GisRequestEnums.GenHazardEnum.HAZARD)) {
            inOutParkChartVO.getGateAccessVoList().addAll(queryAccessGateVoList(closedHazardCarRecordService, ClosedHazardCarRecordEntity::getInOutTime, ClosedHazardCarRecordEntity::getGateId, ClosedHazardCarRecordEntity::getDirection, closeGateList, beginTime, endTime));
            inOutParkChartVO.getPersonCarVoList().addAll(queryPersonCarVoList(closedHazardCarService, closedHazardCarRecordService, ClosedHazardCarEntity::getApplyTime, ClosedHazardCarEntity::getAuditTime, ClosedHazardCarRecordEntity::getInOutTime, ClosedHazardCarRecordEntity::getDirection, beginTime, endTime));
        } else {
            inOutParkChartVO.getGateAccessVoList().addAll(queryAccessGateVoList(closedGeneralCarRecordService, ClosedGeneralCarRecordEntity::getInOutTime, ClosedGeneralCarRecordEntity::getGateId, ClosedGeneralCarRecordEntity::getDirection, closeGateList, beginTime, endTime));
            inOutParkChartVO.getPersonCarVoList().addAll(queryPersonCarVoList(closedGeneralCarService, closedGeneralCarRecordService, ClosedGeneralCarEntity::getApplyTime, ClosedGeneralCarEntity::getAuditTime, ClosedGeneralCarRecordEntity::getInOutTime, ClosedGeneralCarRecordEntity::getDirection, beginTime, endTime));
        }
        return inOutParkChartVO;
    }

    @Override
    public GateCarChartVO queryMultiGateCarChart(String beginTimeStr, String endTimeStr) {
        final Date endTime = DateUtils.parseDate(CommonEnum.StartEndEnum.END, endTimeStr);
        final Date beginTime = DateUtils.parseDate(CommonEnum.StartEndEnum.START, beginTimeStr);
        // 1、危险品车辆卡口统计
        final LambdaQueryWrapper<ClosedHazardCarRecordEntity> hazardWrapper = buildQueryWrapper(ClosedHazardCarRecordEntity::getInOutTime, beginTime, endTime);
        final List<InOutParkVO> hazardInOutParkList = queryMultiGateCarChart(closedHazardCarRecordService, hazardWrapper, ClosedHazardCarRecordEntity::getInOutTime, ClosedHazardCarRecordEntity::getDirection, beginTime, endTime);
        // 2、企业车辆卡口统计
        final LambdaQueryWrapper<ClosedGeneralCarRecordEntity> companyWrapper = buildQueryWrapper(ClosedGeneralCarRecordEntity::getInOutTime, beginTime, endTime).eq(ClosedGeneralCarRecordEntity::getCarType, BasicDicConstant.CLOSE_GENERAL_CAR_TYPE_COMPANY);
        final List<InOutParkVO> companyInOutParkList = queryMultiGateCarChart(closedGeneralCarRecordService, companyWrapper, ClosedGeneralCarRecordEntity::getInOutTime, ClosedGeneralCarRecordEntity::getDirection, beginTime, endTime);
        // 3、访客车辆卡口统计
        final LambdaQueryWrapper<ClosedGeneralCarRecordEntity> visitorWrapper = buildQueryWrapper(ClosedGeneralCarRecordEntity::getInOutTime, beginTime, endTime).eq(ClosedGeneralCarRecordEntity::getCarType, BasicDicConstant.CLOSE_GENERAL_CAR_TYPE_VISITOR);
        final List<InOutParkVO> visitorInOutParkList = queryMultiGateCarChart(closedGeneralCarRecordService, visitorWrapper, ClosedGeneralCarRecordEntity::getInOutTime, ClosedGeneralCarRecordEntity::getDirection, beginTime, endTime);

        final GateCarChartVO resultVO = new GateCarChartVO();
        for (InOutParkVO inOutParkVO : hazardInOutParkList) {
            final String index = inOutParkVO.getIndex();
            final Optional<InOutParkVO> hazardOptional = hazardInOutParkList.stream().filter(e -> e.getIndex().equals(index)).findAny();
            final Optional<InOutParkVO> companyOptional = companyInOutParkList.stream().filter(e -> e.getIndex().equals(index)).findAny();
            final Optional<InOutParkVO> visitorOptional = visitorInOutParkList.stream().filter(e -> e.getIndex().equals(index)).findAny();

            final GateCarVO enterItem = new GateCarVO(index, parseNum(hazardOptional, InOutParkVO::getEnter), parseNum(companyOptional, InOutParkVO::getEnter), parseNum(visitorOptional, InOutParkVO::getEnter));
            final GateCarVO outerItem = new GateCarVO(index, parseNum(hazardOptional, InOutParkVO::getOuter), parseNum(companyOptional, InOutParkVO::getOuter), parseNum(visitorOptional, InOutParkVO::getOuter));

            resultVO.getEnterList().add(enterItem);
            resultVO.getOuterList().add(outerItem);
        }
        return resultVO;
    }

    @Override
    public GateCarChartVO querySingleGateCarChart(Long gateId, String beginTimeStr, String endTimeStr) {
        final Date endTime = DateUtils.parseDate(CommonEnum.StartEndEnum.END, endTimeStr);
        final Date beginTime = DateUtils.parseDate(CommonEnum.StartEndEnum.START, beginTimeStr);
        // 1、危险品车辆卡口统计
        final LambdaQueryWrapper<ClosedHazardCarRecordEntity> hazardWrapper = buildQueryWrapper(ClosedHazardCarRecordEntity::getInOutTime, beginTime, endTime).eq(ClosedHazardCarRecordEntity::getGateId, gateId);
        final List<InOutParkVO> hazardInOutParkList = queryMultiGateCarChart(closedHazardCarRecordService, hazardWrapper, ClosedHazardCarRecordEntity::getInOutTime, ClosedHazardCarRecordEntity::getDirection, beginTime, endTime);
        // 2、企业车辆卡口统计
        final LambdaQueryWrapper<ClosedGeneralCarRecordEntity> companyWrapper = buildQueryWrapper(ClosedGeneralCarRecordEntity::getInOutTime, beginTime, endTime).eq(ClosedGeneralCarRecordEntity::getGateId, gateId)
                .eq(ClosedGeneralCarRecordEntity::getCarType, BasicDicConstant.CLOSE_GENERAL_CAR_TYPE_COMPANY);
        final List<InOutParkVO> companyInOutParkList = queryMultiGateCarChart(closedGeneralCarRecordService, companyWrapper, ClosedGeneralCarRecordEntity::getInOutTime, ClosedGeneralCarRecordEntity::getDirection, beginTime, endTime);
        // 3、访客车辆卡口统计
        final LambdaQueryWrapper<ClosedGeneralCarRecordEntity> visitorWrapper = buildQueryWrapper(ClosedGeneralCarRecordEntity::getInOutTime, beginTime, endTime).eq(ClosedGeneralCarRecordEntity::getGateId, gateId)
                .eq(ClosedGeneralCarRecordEntity::getCarType, BasicDicConstant.CLOSE_GENERAL_CAR_TYPE_VISITOR);
        final List<InOutParkVO> visitorInOutParkList = queryMultiGateCarChart(closedGeneralCarRecordService, visitorWrapper, ClosedGeneralCarRecordEntity::getInOutTime, ClosedGeneralCarRecordEntity::getDirection, beginTime, endTime);

        final GateCarChartVO resultVO = new GateCarChartVO();
        for (InOutParkVO inOutParkVO : hazardInOutParkList) {
            final String index = inOutParkVO.getIndex();
            final Optional<InOutParkVO> hazardOptional = hazardInOutParkList.stream().filter(e -> e.getIndex().equals(index)).findAny();
            final Optional<InOutParkVO> companyOptional = companyInOutParkList.stream().filter(e -> e.getIndex().equals(index)).findAny();
            final Optional<InOutParkVO> visitorOptional = visitorInOutParkList.stream().filter(e -> e.getIndex().equals(index)).findAny();

            final GateCarVO enterItem = new GateCarVO(index, parseNum(hazardOptional, InOutParkVO::getEnter), parseNum(companyOptional, InOutParkVO::getEnter), parseNum(visitorOptional, InOutParkVO::getEnter));
            final GateCarVO outerItem = new GateCarVO(index, parseNum(hazardOptional, InOutParkVO::getOuter), parseNum(companyOptional, InOutParkVO::getOuter), parseNum(visitorOptional, InOutParkVO::getOuter));

            resultVO.getEnterList().add(enterItem);
            resultVO.getOuterList().add(outerItem);
        }
        return resultVO;
    }

    @Override
    public List<EnterOuterVO> queryEnterOuterPark(GisRequestEnums.EnterOuterEnum enterOuterEnum, GisRequestEnums.GisCalEnum gisCalEnum) {
        // 1、处理下标
        Date endTime = null;
        Date beginTime = null;
        Integer calendar = null;
        Function<Date, String> timeHandleMapper = null;
        final Calendar instance = Calendar.getInstance();
        if (gisCalEnum.equals(GisRequestEnums.GisCalEnum.DAY)) {
            calendar = Calendar.HOUR_OF_DAY;
            timeHandleMapper = DateUtils::limitHour;
            endTime = DateUtils.getDateEnd(instance.getTime());
            beginTime = DateUtils.getDateStart(instance.getTime());
        } else if (gisCalEnum.equals(GisRequestEnums.GisCalEnum.MONTH)) {
            calendar = Calendar.DAY_OF_MONTH;
            timeHandleMapper = DateUtils::limitDay;
            final Date[] mothStartEndDate = DateUtils.getMothStartEndDate(instance.getTime());
            endTime = mothStartEndDate[1];
            beginTime = mothStartEndDate[0];
        } else if (gisCalEnum.equals(GisRequestEnums.GisCalEnum.YEAR)) {
            calendar = Calendar.MONTH;
            timeHandleMapper = DateUtils::limitMouth;
            final Date[] yearStartEndDate = DateUtils.getYearStartEndDate(instance.getTime());
            endTime = yearStartEndDate[1];
            beginTime = yearStartEndDate[0];
        }
        final List<String> indexList = handlerDateTime(beginTime, endTime, timeHandleMapper, calendar, 1);
        final List<EnterOuterVO> resList = indexList.stream().filter(StrUtil::isNotBlank).map(EnterOuterVO::new).collect(Collectors.toList());
        if (enterOuterEnum.equals(GisRequestEnums.EnterOuterEnum.ENTER)) {
            return handleEnterOuterVOList(resList, timeHandleMapper, instance, gisCalEnum, VisitorDicConstant.CLOSE_VISITOR_DIRECTION_IN);
        } else {
            return handleEnterOuterVOList(resList, timeHandleMapper, instance, gisCalEnum, VisitorDicConstant.CLOSE_VISITOR_DIRECTION_OUT);
        }
    }

    @Override
    public EnterOuterChartVO queryEnterOuterChart(String beginTimeStr, String endTimeStr) {
        final Date endTime = DateUtils.parseDate(CommonEnum.StartEndEnum.END, endTimeStr);
        final Date beginTime = DateUtils.parseDate(CommonEnum.StartEndEnum.START, beginTimeStr);
        int calendar = Calendar.HOUR_OF_DAY;
        Function<Date, String> timeHandleMapper = DateUtils::limitHour;
        if (DateUtils.getIntervalDays(beginTime, endTime) > parkCloseProperty.getGis().getChartHourToDay()) {
            calendar = Calendar.DAY_OF_YEAR;
            timeHandleMapper = DateUtils::limitDay;
        }
        final List<String> indexList = handlerDateTime(beginTime, endTime, timeHandleMapper, calendar, 1);
        final List<EnterOuterVO> resList = indexList.stream().filter(StrUtil::isNotBlank).map(EnterOuterVO::new).collect(Collectors.toList());
        final List<EnterOuterVO> enterVOList = handleEnterOuterChartVOList(resList, timeHandleMapper, beginTime, endTime, VisitorDicConstant.CLOSE_VISITOR_DIRECTION_IN);
        final List<EnterOuterVO> outerVOList = handleEnterOuterChartVOList(resList, timeHandleMapper, beginTime, endTime, VisitorDicConstant.CLOSE_VISITOR_DIRECTION_OUT);

        return new EnterOuterChartVO(enterVOList, outerVOList);
    }

    private List<EnterOuterVO> handleEnterOuterChartVOList(List<EnterOuterVO> resList, Function<Date, String> timeHandleMapper, Date beginTime, Date endTime, Integer visitorDirection) {
        // 1、危化品车辆
        final LambdaQueryWrapper<ClosedHazardCarRecordEntity> hazardWrapper = buildQueryWrapper(ClosedHazardCarRecordEntity::getInOutTime, beginTime, endTime).eq(ClosedHazardCarRecordEntity::getDirection, visitorDirection);
        final List<ClosedHazardCarRecordEntity> hazardRecordList = closedHazardCarRecordService.list(hazardWrapper);

        // 2、普通车辆
        final LambdaQueryWrapper<ClosedGeneralCarRecordEntity> generalWrapper = buildQueryWrapper(ClosedGeneralCarRecordEntity::getInOutTime, beginTime, endTime).eq(ClosedGeneralCarRecordEntity::getDirection, visitorDirection);
        final List<ClosedGeneralCarRecordEntity> generalRecordList = closedGeneralCarRecordService.list(generalWrapper);

        // 3、园区人员
        final LambdaQueryWrapper<ClosedPersonRecordEntity> personWrapper = buildQueryWrapper(ClosedPersonRecordEntity::getInOutTime, beginTime, endTime).eq(ClosedPersonRecordEntity::getDirection, visitorDirection);
        final List<ClosedPersonRecordEntity> personRecordList = closedPersonRecordService.list(personWrapper);

        for (EnterOuterVO enterOuterVO : resList) {
            final String index = enterOuterVO.getIndex();

            final long hazardNum = hazardRecordList.stream().filter(e -> ObjUtil.isNotNull(e.getInOutTime())).filter(e -> timeHandleMapper.apply(e.getInOutTime()).equals(index)).count();
            final long personNum = personRecordList.stream().filter(e -> ObjUtil.isNotNull(e.getInOutTime())).filter(e -> timeHandleMapper.apply(e.getInOutTime()).equals(index)).count();
            final long generalNum = generalRecordList.stream().filter(e -> ObjUtil.isNotNull(e.getInOutTime())).filter(e -> timeHandleMapper.apply(e.getInOutTime()).equals(index)).count();

            enterOuterVO.setHazardNum(Long.valueOf(hazardNum).intValue());
            enterOuterVO.setPersonNum(Long.valueOf(personNum).intValue());
            enterOuterVO.setGeneralNum(Long.valueOf(generalNum).intValue());
        }
        return resList;
    }

    /**
     *
     * @param resList           结果集
     * @param timeHandleMapper  时间处理对象
     * @param instance          时间
     * @param gisCalEnum        统计纬度
     * @param visitorDirection  进出方向
     * @return
     */
    private List<EnterOuterVO> handleEnterOuterVOList(List<EnterOuterVO> resList, Function<Date, String> timeHandleMapper, Calendar instance, GisRequestEnums.GisCalEnum gisCalEnum, Integer visitorDirection) {
        // 1、危化品车辆
        final LambdaQueryWrapper<ClosedHazardCarRecordEntity> hazardWrapper = buildQueryWrapper(ClosedHazardCarRecordEntity::getInOutTime, instance, gisCalEnum).eq(ClosedHazardCarRecordEntity::getDirection, visitorDirection);
        final List<ClosedHazardCarRecordEntity> hazardRecordList = closedHazardCarRecordService.list(hazardWrapper);

        // 2、普通车辆
        final LambdaQueryWrapper<ClosedGeneralCarRecordEntity> generalWrapper = buildQueryWrapper(ClosedGeneralCarRecordEntity::getInOutTime, instance, gisCalEnum).eq(ClosedGeneralCarRecordEntity::getDirection, visitorDirection);
        final List<ClosedGeneralCarRecordEntity> generalRecordList = closedGeneralCarRecordService.list(generalWrapper);

        // 3、园区人员
        final LambdaQueryWrapper<ClosedPersonRecordEntity> personWrapper = buildQueryWrapper(ClosedPersonRecordEntity::getInOutTime, instance, gisCalEnum).eq(ClosedPersonRecordEntity::getDirection, visitorDirection);
        final List<ClosedPersonRecordEntity> personRecordList = closedPersonRecordService.list(personWrapper);

        for (EnterOuterVO enterOuterVO : resList) {
            final String index = enterOuterVO.getIndex();

            final long hazardNum = hazardRecordList.stream().filter(e -> ObjUtil.isNotNull(e.getInOutTime())).filter(e -> timeHandleMapper.apply(e.getInOutTime()).equals(index)).count();
            final long personNum = personRecordList.stream().filter(e -> ObjUtil.isNotNull(e.getInOutTime())).filter(e -> timeHandleMapper.apply(e.getInOutTime()).equals(index)).count();
            final long generalNum = generalRecordList.stream().filter(e -> ObjUtil.isNotNull(e.getInOutTime())).filter(e -> timeHandleMapper.apply(e.getInOutTime()).equals(index)).count();

            enterOuterVO.setHazardNum(Long.valueOf(hazardNum).intValue());
            enterOuterVO.setPersonNum(Long.valueOf(personNum).intValue());
            enterOuterVO.setGeneralNum(Long.valueOf(generalNum).intValue());
        }
        return resList;
    }


    private Integer parseNum(Optional<InOutParkVO> optional, Function<InOutParkVO, Integer> mapper) {
        return optional.map(mapper).orElse(0);
    }


    /**
     * 查询卡口进车数据
     * @param service    服务
     * @param queryWrapper    查询条件
     * @return
     */
    private <T, R> List<InOutParkVO> queryMultiGateCarChart(IService<T> service, Wrapper<T> queryWrapper, SFunction<T, Date> inOutDataMapper, SFunction<T, R> groupMapper, Date beginTime, Date endTime) {
        final List<T> recordList = service.list(queryWrapper);
        final List<HazardCarBO<R>> hazardCarBOList = handlerQueryList(recordList, inOutDataMapper, groupMapper, beginTime, endTime);
        final List<InOutParkVO> resList = new ArrayList<>();
        for (HazardCarBO<R> hazardCarBO : hazardCarBOList) {
            final String index = hazardCarBO.getIndex(); // 下标
            final Integer enterNum = MapUtil.getInt(hazardCarBO.getGroupMap(), VisitorDicConstant.CLOSE_VISITOR_DIRECTION_IN, 0);
            final Integer outerNum = MapUtil.getInt(hazardCarBO.getGroupMap(), VisitorDicConstant.CLOSE_VISITOR_DIRECTION_OUT, 0);
            resList.add(new InOutParkVO(index, 0, 0, enterNum, outerNum, 0));
        }
        return resList;
    }



    /**
     * 查询危险品车辆 入园、出园、申请、审批车次
     * @param beginTime    开始时间
     * @param endTime      截止时间
     * @return             结果集
     */
    private <T, R, F> Collection<? extends InOutParkVO> queryPersonCarVoList(IService<T> service, IService<F> recordService, SFunction<T, Date> applyMapper, SFunction<T, Date> auditMapper, SFunction<F, Date> inOutTimeMapper, Function<F, Integer> directionMapper, Date beginTime, Date endTime) {
        final List<T> applyList = service.list(buildQueryWrapper(applyMapper, beginTime, endTime));
        final List<HazardCarBO<Object>> applyBOList = handlerQueryList(applyList, applyMapper, null, beginTime, endTime);

        final List<T> auditList = service.list(buildQueryWrapper(auditMapper, beginTime, endTime));
        final List<HazardCarBO<Object>> auditBOList = handlerQueryList(auditList, auditMapper, null, beginTime, endTime);

        final List<F> enterOuterList = recordService.list(buildQueryWrapper(inOutTimeMapper, beginTime, endTime));
        final List<HazardCarBO<Integer>> hazardCarBOList = handlerQueryList(enterOuterList, inOutTimeMapper, directionMapper, beginTime, endTime);

        final List<InOutParkVO> resList = new ArrayList<>();
        for (HazardCarBO<Integer> hazardCarBO : hazardCarBOList) {
            final String index = hazardCarBO.getIndex();
            final Integer enterNum = MapUtil.getInt(hazardCarBO.getGroupMap(), VisitorDicConstant.CLOSE_VISITOR_DIRECTION_IN, 0);
            final Integer outerNum = MapUtil.getInt(hazardCarBO.getGroupMap(), VisitorDicConstant.CLOSE_VISITOR_DIRECTION_OUT, 0);
            final Long applyNum = applyBOList.stream().filter(e -> e.getIndex().equals(index)).findFirst().map(HazardCarBO::getCount).orElse(0L);
            final Long auditNum = auditBOList.stream().filter(e -> e.getIndex().equals(index)).findFirst().map(HazardCarBO::getCount).orElse(0L);
            resList.add(new InOutParkVO(index, applyNum.intValue(), auditNum.intValue(), enterNum, outerNum, 0));
        }
        return resList;
    }

    /**
     * 查询出入园车次 卡口统计
     * @param beginTime   开始时间
     * @param endTime     截止时间
     * @return            结果集
     */
    private <T> Collection<? extends InOutParkVO> queryAccessGateVoList(IService<T> recordService, SFunction<T, Date> dateMapper, Function<T, Long> gateMapper,  Function<T, Integer> directionMapper, List<SysDictItem> sysDictItemList, Date beginTime, Date endTime) {
        final List<InOutParkVO> resList = new ArrayList<>();
        final List<T> gateRecordList = recordService.list(buildQueryWrapper(dateMapper, beginTime, endTime));
        final Map<Long, List<T>> gateListMap = gateRecordList.stream().filter(e -> ObjUtil.isNotNull(gateMapper.apply(e))).collect(Collectors.groupingBy(gateMapper));
        for (Map.Entry<Long, List<T>> entry : gateListMap.entrySet()) {
            final Long key = entry.getKey();
            final Map<Integer, Long> longMap = entry.getValue().stream().filter(e -> ObjUtil.isNotNull(directionMapper.apply(e))).collect(Collectors.groupingBy(directionMapper, Collectors.counting()));
            final InOutParkVO inOutParkVO = new InOutParkVO();
            inOutParkVO.setIndex(sysDictItemList.stream().filter(e -> e.getId().equals(key)).findFirst().map(SysDictItem::getName).orElse(null));
            inOutParkVO.setEnter(MapUtil.getInt(longMap, VisitorDicConstant.CLOSE_VISITOR_DIRECTION_IN, 0));
            inOutParkVO.setOuter(MapUtil.getInt(longMap, VisitorDicConstant.CLOSE_VISITOR_DIRECTION_OUT, 0));
            resList.add(inOutParkVO);
        }
        return resList;
    }


    /**
     * 处理查询列表数据
     * @param queryList      查询列表
     * @param mapper         映射字段
     * @param groupMapper    分组映射字段
     * @param beginTime      开始时间
     * @param endTime        截止时间
     * @return               返回值
     */
    private <T, R> List<HazardCarBO<R>> handlerQueryList(List<T> queryList, SFunction<T, Date> mapper, Function<T, R> groupMapper, Date beginTime, Date endTime) {
        int calendar = Calendar.HOUR_OF_DAY;
        Function<Date, String> timeHandleMapper = DateUtils::limitHour;
        if (DateUtils.getIntervalDays(beginTime, endTime) > parkCloseProperty.getGis().getChartHourToDay()) {
            calendar = Calendar.DAY_OF_YEAR;
            timeHandleMapper = DateUtils::limitDay;
        }
        final List<HazardCarBO<R>> resList = new ArrayList<>();
        final List<String> indexList = handlerDateTime(beginTime, endTime, timeHandleMapper, calendar, 1);
        for (String index : indexList) {
            Function<Date, String> finalTimeHandleMapper = timeHandleMapper;
            if (ObjUtil.isNull(groupMapper)) {
                final Long count = queryList.parallelStream().filter(e -> ObjUtil.isNotNull(mapper.apply(e))).filter(e -> finalTimeHandleMapper.apply(mapper.apply(e)).equals(index)).count();
                resList.add(new HazardCarBO<R>(index, count));
            } else {
                final Map<R, Long> collect = queryList.parallelStream().filter(e -> ObjUtil.isNotNull(mapper.apply(e))).filter(e -> finalTimeHandleMapper.apply(mapper.apply(e)).equals(index)).collect(Collectors.groupingBy(groupMapper, Collectors.counting()));
                resList.add(new HazardCarBO<R>(index, collect));
            }
        }
        return resList;
    }


    /**
     * 出入园车次
     * @param service        服务
     * @param dateMapper     时间映射
     * @param queryMapper    查询映射
     * @param instance       时间
     * @param gisCalEnum     查询参数
     */
    private <T> Map<Integer, Integer> queryEnterOuterVO(IService<T> service, SFunction<T, Date> dateMapper, SFunction<T, ?> queryMapper, Calendar instance, GisRequestEnums.GisCalEnum gisCalEnum) {
        final LambdaQueryWrapper<T> enterWrapper = buildQueryWrapper(dateMapper, instance, gisCalEnum);
        enterWrapper.eq(queryMapper, VisitorDicConstant.CLOSE_VISITOR_DIRECTION_IN);
        final long enter = service.count(enterWrapper);

        final LambdaQueryWrapper<T> outerWrapper = buildQueryWrapper(dateMapper, instance, gisCalEnum);
        outerWrapper.eq(queryMapper, VisitorDicConstant.CLOSE_VISITOR_DIRECTION_OUT);
        final long outer = service.count(outerWrapper);

        return new HashMap() {
            private static final long serialVersionUID = -8587449741991353248L;
            {
                put(VisitorDicConstant.CLOSE_VISITOR_DIRECTION_IN, Long.valueOf(enter).intValue());
                put(VisitorDicConstant.CLOSE_VISITOR_DIRECTION_OUT, Long.valueOf(outer).intValue());
            }
        };
    }

    /**
     * 申请车次、审批车次
     * @param service         服务层对象
     * @param applyWrapper    申请查询条件
     * @param auditWrapper    审批查询条件
     * @param insideFlag      是否查询内部
     * @param insideWrapper   内部人员查询条件
     */
    private <T> ApplyAuditBO queryApplyAuditVO(IService<T> service, Wrapper<T> applyWrapper, Wrapper<T> auditWrapper, boolean insideFlag, Wrapper<T> insideWrapper) {
        final ApplyAuditBO applyAuditBO = new ApplyAuditBO();
        final long apply = service.count(applyWrapper);
        final long audit = service.count(auditWrapper);
        applyAuditBO.setApply(Long.valueOf(apply).intValue());
        applyAuditBO.setAudit(Long.valueOf(audit).intValue());
        if (insideFlag) {
            final long inside = service.count(insideWrapper);
            applyAuditBO.setInside(Long.valueOf(inside).intValue());
        }
       return applyAuditBO;
    }

    private <T> LambdaQueryWrapper<T> buildQueryWrapper(SFunction<T, Date> mapper, Date beginTime, Date endTime) {
        return Wrappers.<T>lambdaQuery().gt(mapper, beginTime).lt(mapper, endTime);
    }

    private <T> LambdaQueryWrapper<T> buildQueryWrapper(SFunction<T, Date> mapper, Calendar instance, GisRequestEnums.GisCalEnum gisCalEnum) {
        final LambdaQueryWrapper<T> queryWrapper = Wrappers.lambdaQuery();
        if (gisCalEnum.equals(GisRequestEnums.GisCalEnum.DAY)) {
            queryWrapper.gt(mapper, DateUtils.dateLimitDay(instance.getTime()));
        } else if (gisCalEnum.equals(GisRequestEnums.GisCalEnum.MONTH)) {
            queryWrapper.gt(mapper, DateUtils.dateLimitMonth(instance.getTime()));
        } else if (gisCalEnum.equals(GisRequestEnums.GisCalEnum.YEAR)) {
            queryWrapper.gt(mapper, DateUtils.parseDate(String.valueOf(instance.get(Calendar.YEAR)), DateUtils.FORMAT_YEAR));
        }
        return queryWrapper;
    }

    /**
     * 处理时间
     *
     * @param beginTime 开始时间
     * @param endTime   结束时间
     * @param mapper    函数，格式化时间
     * @param calendar  类型 如: Calendar.DAY_OF_YEAR, Calendar.HOUR_OF_DAY 等
     * @param step      步长
     * @return 结果
     */
    private List<String> handlerDateTime(Date beginTime, Date endTime, Function<Date, String> mapper, Integer calendar, Integer step) {
        final List<String> list = new ArrayList<>();
        final Calendar instance = Calendar.getInstance();
        instance.setTime(beginTime);
        while (instance.getTimeInMillis() < endTime.getTime()) {
            final String apply = mapper.apply(instance.getTime());
            list.add(apply);
            instance.add(calendar, step);
        }
        return list;
    }




}
